package service

import (
	"encoding/json"
	"github.com/mitchellh/mapstructure"
	"gorm.io/gorm"
	"view-crawler-backend/internal/bo"
	"view-crawler-backend/internal/constant"
	"view-crawler-backend/internal/dao"
	"view-crawler-backend/internal/dao/entity"
	"view-crawler-backend/internal/service/task_executor"
	"view-crawler-backend/internal/tools"
)

func TaskExec(taskId int64, msgSendStream chan string, dataSteam chan map[string]any, done chan struct{}, connDone chan bool, controlStream chan bool) {
	msgSendStream <- tools.LogFormat(constant.WHITE, constant.TASK_INIT, "开始进行任务初始化")

	// 读取任务定义
	taskDefine, err := dao.QueryTaskDefineById(taskId)
	if err != nil {
		msgSendStream <- tools.LogFormat(constant.RED, constant.TASK_INIT, "任务定义读取失败")
		connDone <- false
		return
	}

	// 将json格式的任务定义解析为struct
	taskExec := bo.TaskExec{
		TaskId: taskId,
	}
	if json.Unmarshal([]byte(taskDefine), &taskExec) != nil {
		msgSendStream <- tools.LogFormat(constant.RED, constant.TASK_INIT, "任务定义解析失败")
		connDone <- false
		return
	}
	if err := map2struct(taskExec.Nodes, msgSendStream); err != nil {
		msgSendStream <- tools.LogFormat(constant.RED, constant.TASK_INIT, "任务定义解析失败")
		connDone <- false
		return
	}

	// 初始化数据存储
	if err := initDataPersistence(&taskExec); err != nil {
		msgSendStream <- tools.LogFormat(constant.RED, constant.TASK_INIT, "数据存储初始化失败")
		connDone <- false
		return
	}

	// 创建任务执行器
	taskExecutor, err := task_executor.NewTaskExecutor(&taskExec, msgSendStream, dataSteam, done, controlStream)
	if err != nil {
		msgSendStream <- tools.LogFormat(constant.RED, constant.TASK_INIT, "任务执行器创建失败")
		connDone <- false
		return
	}

	taskExecutor.Exec()
}

// map2struct 递归将nodes中的map转换为相应的struct
func map2struct(nodes []any, msgSendStream chan string) error {
	for i := 0; i < len(nodes); i++ {
		mp := nodes[i].(map[string]any)
		// 修正数值类型（默认json解码后数值类型为float64）
		for k, v := range mp {
			if v, ok := v.(float64); ok {
				mp[k] = int(v)
			}
		}
		switch mp["type"].(int) {
		case constant.NodeTypeOpenPage:
			node := bo.OpenPageNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			nodes[i] = &node
		case constant.NodeTypeClickElement:
			node := bo.ClickElementNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			nodes[i] = &node
		case constant.NodeTypeExtractData:
			node := bo.ExtractDataNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			nodes[i] = &node
		case constant.NodeTypeLoop:
			node := bo.LoopNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			if err := map2struct(node.SubNodes, msgSendStream); err != nil {
				return err
			}
			nodes[i] = &node
		case constant.NodeTypeCondition:
			node := bo.ConditionNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			if err := map2struct(node.SubNodes, msgSendStream); err != nil {
				return err
			}
			nodes[i] = &node
		case constant.NodeTypeBranch:
			node := bo.BranchNode{}
			if err := mapstructure.Decode(nodes[i], &node); err != nil {
				return err
			}
			if err := map2struct(node.SubNodes, msgSendStream); err != nil {
				return err
			}
			nodes[i] = &node
		}
	}
	return nil
}

// initDataPersistence 如果必要的话创建用于存储该任务数据的表
// 该表的字段与爬取的数据的字段一一对应，额外添加一个字段表示是第几次执行的数据
// 默认只保存一个任务最近10次的记录数据，超过十次时还需要删除最早的记录
func initDataPersistence(taskExec *bo.TaskExec) error {
	// 获取当前任务的历史执行次数
	execTimes, err := dao.QueryTaskExecTimesById(dao.DB, taskExec.TaskId)
	if err != nil {
		return err
	}

	if err := dao.DB.Transaction(func(tx *gorm.DB) error {
		// 增加一条任务执行记录。执行ID为当前执行次数
		taskExecRecord := entity.TaskExecRecord{
			TaskId: taskExec.TaskId,
			ExecId: execTimes,
		}
		if err := dao.AddTaskExecRecord(tx, &taskExecRecord); err != nil {
			return err
		}

		// 更新Task表中该任务的执行次数
		if err := dao.IncreaseTaskExecTimes(tx, taskExec.TaskId); err != nil {
			return err
		}
		// 执行次数过多，需要删除最早一次的执行数据
		if execTimes >= 10 {
			// 获取最早一次的执行id,并删除该次执行记录（task_exec表）
			execId, err := dao.DeleteOldestTaskExecRecord(tx, taskExec.TaskId)
			if err != nil {
				return err
			}
			// 删除该次执行的数据
			if err := dao.DeleteExecData(tx, taskExec.TaskId, execId); err != nil {
				return err
			}
		} else if execTimes == 0 {
			// 第一次执行，需要建表
			if err := dao.CreateExecDataTable(tx, taskExec.TaskId, taskExec.DataFieldList); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}
